# python代码的执行过程
# 导入 ast 模块，用于处理抽象语法树
import ast

# 导入 dis 模块，用于反汇编字节码
import dis

# 定义一段源代码，内容为：a=1 和 print(a)
source_code = """
a = 10
b = 20
c = a + b
"""

print("1. 源代码:")
# 打印上面定义的源代码
print(source_code)

print("2. 抽象语法树(AST):")
try:
    # 调用 ast.parse 解析源代码为 AST 对象
    tree = ast.parse(source_code)
    # 用 ast.dump 打印整个 AST 结构，并设置缩进为2
    print(ast.dump(tree, indent=2))
except SyntaxError as e:
    # 如果解析过程中抛出语法错误，打印相应信息
    print(f"语法错误: {e}")

print("\n3. 字节码:")
try:
    # 使用 compile 函数将源代码编译为字节码对象，文件名设置为 <string>，模式为 exec
    code_obj = compile(source_code, "<string>", "exec")
    # 打印刚才获得的字节码对象的信息
    print(code_obj)
    # 调用 dis.dis 反汇编字节码，输出详细的字节码指令列表
    dis.dis(code_obj)
except SyntaxError as e:
    # 如果编译源代码时出现语法错误，打印提示信息
    print(f"编译错误: {e}")

print("\n4. 执行结果:")
try:
    # 用 exec 执行上面 compile 出来的字节码对象
    exec(code_obj)
except Exception as e:
    # 如果在执行过程中抛出异常，打印错误提示
    print(f"执行错误: {e}")


# 定义一个简单的虚拟机类
class SimpleVM:
    """Python虚拟机"""

    # 初始化方法
    def __init__(self):
        # 初始化操作数栈
        self.stack = []  # 操作数栈 - 存储计算过程中的临时数据
        # 初始化局部变量字典
        self.locals = {}  # 局部变量 - 存储函数内的变量
        # 初始化全局变量字典
        self.globals = {}  # 全局变量 - 存储全局变量

    # 加载常量到操作数栈顶 把数字、字符串等常量放到栈顶
    # load_const(10) 会把数字10放到栈里
    def load_const(self, const):
        # 将常量值加入到栈顶
        self.stack.append(const)
        # 输出调试信息
        print(f"加载常量: {const} -> 栈: {self.stack}")

    # 加载变量的值到操作数栈顶 根据变量名找到对应的值，然后放到栈顶
    # load_name('a') 会找到变量a的值，放到栈里
    def load_name(self, name):
        # 如果变量在局部变量字典中
        if name in self.locals:
            value = self.locals[name]
        # 如果变量在全局变量字典中
        elif name in self.globals:
            value = self.globals[name]
        # 变量不存在，则抛出异常
        else:
            raise NameError(f"name '{name}' is not defined")
        # 将变量值加入到栈顶
        self.stack.append(value)
        # 输出调试信息
        print(f"加载变量: {name} = {value} -> 栈: {self.stack}")

    # 将操作数栈顶的值存储到指定变量 把栈顶的值取出来，存储到指定的变量中
    # store_name('a') 会把栈顶的值存到变量a中
    def store_name(self, name):
        # 如果栈为空，则抛出异常
        if not self.stack:
            raise RuntimeError("栈为空")
        # 取出栈顶值
        value = self.stack.pop()
        # 存储到局部变量字典中
        self.locals[name] = value
        # 输出调试信息
        print(f"存储变量: {name} = {value}")

    # 执行加法操作 从栈顶取出两个数，相加后把结果放回栈顶
    # 栈里有[10, 20]，执行后变成[30]
    def binary_add(self):
        # 检查操作数栈中是否有2个及以上元素
        if len(self.stack) < 2:
            raise RuntimeError("栈中元素不足")
        # 弹出右操作数
        b = self.stack.pop()
        # 弹出左操作数
        a = self.stack.pop()
        # 执行加法计算结果
        result = a + b
        # 结果压栈
        self.stack.append(result)
        # 输出调试信息
        print(f"加法: {a} + {b} = {result} -> 栈: {self.stack}")

    # 返回操作数栈顶的值 从栈顶取出值并返回
    def return_value(self):
        # 如果栈为空，返回None
        if not self.stack:
            return None
        # 弹出并返回栈顶值
        value = self.stack.pop()
        # 输出调试信息
        print(f"返回: {value}")
        return value


# 演示虚拟机指令执行过程的函数
def vm_execution():
    # 创建虚拟机实例
    vm = SimpleVM()

    # 输出即将执行的代码说明
    print("执行代码: a = 10; b = 20; c = a + b")

    # 构造指令序列
    instructions = [
        ("LOAD_CONST", 10),  # 加载常量10
        ("STORE_NAME", "a"),  # 存储到变量a
        ("LOAD_CONST", 20),  # 加载常量20
        ("STORE_NAME", "b"),  # 存储到变量b
        ("LOAD_NAME", "a"),  # 加载变量a
        ("LOAD_NAME", "b"),  # 加载变量b
        ("BINARY_ADD",),  # 执行加法
        ("STORE_NAME", "c"),  # 存储到变量c
        ("LOAD_NAME", "c"),  # 加载变量c
        ("RETURN_VALUE",),  # 返回值
    ]

    # 依次执行指令
    for instruction in instructions:
        # 取操作码
        op = instruction[0]
        # 取操作数参数
        args = instruction[1:] if len(instruction) > 1 else []

        # 根据操作码分发指令
        if op == "LOAD_CONST":
            vm.load_const(args[0])
        elif op == "STORE_NAME":
            vm.store_name(args[0])
        elif op == "LOAD_NAME":
            vm.load_name(args[0])
        elif op == "BINARY_ADD":
            vm.binary_add()
        elif op == "RETURN_VALUE":
            result = vm.return_value()
            # 返回值后跳出循环
            break

    # 输出最终结果
    print(f"\n最终结果: c = {result}")


# 执行虚拟机演示
vm_execution()

"""
执行步骤：
1、.py源码文件
2、编译
3、字节码文件.pyc文件
4、python虚拟机PVM加载字节码
5、循环开始，逐条取出字节码指令
6、解释并执行对应操作
7、指令执行完毕
8、输出程序结果
"""
